// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include <stdio.h>
#include <iostream>
#include "network.h"
#ifdef NETWORK
#include "post.h"
#endif

#define SUCCESS 999
//#include "thread.h"
//#include "process.h"
//#include "processtable.h"

using namespace std;

int copyin(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
    bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;

    while ( n >= 0 && n < len) {
      result = machine->ReadMem( vaddr, 1, paddr );
      while(!result) // FALL 09 CHANGES
	  {
   			result = machine->ReadMem( vaddr, 1, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
	  }	
      
      buf[n++] = *paddr;
     
      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    delete paddr;
    return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
    bool result;
    int n=0;			// The number of bytes copied in

    while ( n >= 0 && n < len) {
      // Note that we check every byte's address
      result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );

      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Create\n");
	delete buf;
	return;
    }

    buf[len]='\0';

    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id

    if (!buf) {
	printf("%s","Can't allocate kernel buffer in Open\n");
	return -1;
    }

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Open\n");
	delete[] buf;
	return -1;
    }

    buf[len]='\0';

    f = fileSystem->Open(buf);
    delete[] buf;

    if ( f ) {
	if ((id = currentThread->space->fileTable.Put(f)) == -1 )
	    delete f;
	return id;
    }
    else
	return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
    
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output

    if ( id == ConsoleInput) return;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer for write!\n");
	return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
	    printf("%s","Bad pointer passed to to write: data not written\n");
	    delete[] buf;
	    return;
	}
    }

    if ( id == ConsoleOutput) {
      for (int ii=0; ii<len; ii++) {
	printf("%c",buf[ii]);
      }

    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    f->Write(buf, len);
	} else {
	    printf("%s","Bad OpenFileId passed to Write\n");
	    len = -1;
	}
    }

    delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output

    if ( id == ConsoleOutput) return -1;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer in Read\n");
	return -1;
    }

    if ( id == ConsoleInput) {
      //Reading from the keyboard
      scanf("%s", buf);

      if ( copyout(vaddr, len, buf) == -1 ) {
	printf("%s","Bad pointer passed to Read: data not copied\n");
      }
    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    len = f->Read(buf, len);
	    if ( len > 0 ) {
	        //Read something from the file. Put into user's address space
  	        if ( copyout(vaddr, len, buf) == -1 ) {
		    printf("%s","Bad pointer passed to Read: data not copied\n");
		}
	    }
	} else {
	    printf("%s","Bad OpenFileId passed to Read\n");
	    len = -1;
	}
    }

    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);

    if ( f ) {
      delete f;
    } else {
      printf("%s","Tried to close an unopen file\n");
    }
}

//#ifdef CHANGED
void Exit_syscall(int exitID) {
	Process* currentProcess;
	AddrSpace* currentSpace;
	//int processes, threads, activeThreads, waitThreads, signalThreads, currentProcessID, currentThreadID;

	printf("exitID: %d\n", exitID);
	syscallLock->Acquire();

	currentProcess = currentThread->process;
	currentSpace = currentThread->space;
	//currentProcessID = currentProcess->getProcessID();
	//currentThreadID = currentThread->threadID;

	/*
	processTableLock->Acquire();
	threads = currentProcess->getThreadCount();
	activeThreads = currentProcess->getActiveThreadCount();
	waitThreads = currentProcess->getWaitThreadCount();
	signalThreads = currentProcess->getSignalThreadCount();
	processTableLock->Release();
	*/

	// if there are more than 1 processes (ie, other processes through Exec_syscall
	// from main program/process, then can safely call thread->finish();
	if (processTable->getProcessCount() > 1) {
		// if there are still other threads running for this process, just finish
		// this one
		if (currentProcess->getActiveThreadCount() > 1 || (currentProcess->getActiveThreadCount() == 1 && currentProcess->getSignalThreadCount() > 0 && currentProcess->getWaitThreadCount() > 0)) {
		//if (currentProcess->getThreadCount() > 1) {
			DEBUG('z', "[INFO][Exit_syscall](A) Removing Thread %d from Process %d.\n", currentThread->threadID, currentProcess->getProcessID());
			//DEBUG('z', "[INFO][Exit_syscall](A) Removing Thread %d: %s from Process %d: %s.\n", currentThread->threadID, currentThread->getName(), currentProcess->getProcessID(), currentProcess->getName());
			currentProcess->removeThread(currentThread->threadID);
			currentThread->space->destroyStack(currentThread->threadID);
			syscallLock->Release();
			//processTableLock->Release();
			currentThread->Finish();
		}
		// if it's the last thread in the process, we have to clearn up the data
		else {
			//if there are other threads in the process that are stuck, then what do we do?
			//nothing to worry, since when deleteing the AddrSpace, it will check all the valid memory spaces and delete
			if (currentProcess->getThreadCount() > 1) {
				DEBUG('z', "[INFO][Exit_syscall] There are threads in BLOCKED status for Process %d, but the process is going to be removed.\n", currentProcess->getProcessID());
				//DEBUG('z', "[INFO][Exit_syscall] There are threads in BLOCKED status for Process %d: %s, but process is going to be removed.\n", currentProcess->getProcessID(), currentProcess->getName());
				printf("[INFO][Exit_syscall] There are threads in BLOCKED status for Process %d, but the process is going to be removed.\n", currentProcess->getProcessID());
			}

			DEBUG('z', "[INFO][Exit_syscall](B) Removing Process %d with Thread %d.\n", currentProcess->getProcessID(), currentThread->threadID);
			//DEBUG('z', "[INFO][Exit_syscall](B) Removing Process %d: %s with Thread %d: %s.\n", currentProcess->getProcessID(), currentProcess->getName(), currentThread->threadID, currentThread->getName());
			currentProcess->removeThread(currentThread->threadID);
			currentThread->space->destroyStack(currentThread->threadID);
			processTable->removeProcess(currentProcess->getProcessID());
			DEBUG('z', "[INFO][Exit_syscall](B) # of processes left: %d.\n", processTable->getProcessCount());

			//kernelLocksConditionsLock->Acquire();
			//kernelConditionsLock->Acquire();
			for (int i = 0; i < MAX_CONDITIONS; i++) {
				if (!kernelConditionsMap->Test(i)) {
					continue;
				}

				if (kernelConditions[i].addrSpace == currentSpace && kernelConditions[i].isToBeDeleted == TRUE) {
					if (kernelConditions[i].condition->isBusy()) {
						DEBUG('e', "[CAUTION][Exit_syscall] Condition ID [%d] is being used in Process %d.\n", i, currentProcess->getProcessID());
					}
					DEBUG('e', "[INFO][Exit_syscall] Condition ID [%d] is marked to be deleted, so deleting for Process %d.\n", i, currentProcess->getProcessID());
					delete kernelConditions[i].condition;
					kernelConditions[i].condition = NULL;
					kernelConditions[i].addrSpace = NULL;
					kernelConditions[i].isToBeDeleted = FALSE;
				}
			}	
			//kernelConditionsLock->Release();

			//kernelLocksLock->Acquire();
			for (int i = 0; i < MAX_LOCKS; i++) {
				if (!kernelLocksMap->Test(i)) {
					continue;
				}

				if (kernelLocks[i].addrSpace == currentSpace && kernelLocks[i].isToBeDeleted == TRUE) {
					if (kernelLocks[i].lock->isBusy()) {
						DEBUG('e', "[CAUTION][Exit_syscall] Lock ID [%d] is being used in Process %d.\n", i, currentProcess->getProcessID());
					}
					DEBUG('e', "[INFO][Exit_syscall] Lock ID [%d] is marked to be deleted, so deleting for Process %d.\n", i, currentProcess->getProcessID());
					delete kernelLocks[i].lock;
					kernelLocks[i].lock = NULL;
					kernelLocks[i].addrSpace = NULL;
					kernelLocks[i].isToBeDeleted = FALSE;
				}
			}
			//kernelLocksLock->Release();
			//kernelLocksConditionsLock->Release();

			delete currentSpace;
			delete currentProcess;
			//printf("WE ARE HERE 2...\n");
			syscallLock->Release();
			//processTableLock->Release();

			currentThread->Finish();
		}
	}
	// if only the main program/process remains, have to check for the chance that
	// we have to call interrupt->Halt();
	else {
		// if there are still other threads running for this process, just finish
		// this one
		//if (activeThreads > 1 || (activeThreads == 1 && signalThreads > 0 && waitThreads > 0)) {
		if (currentProcess->getActiveThreadCount() > 1 || (currentProcess->getActiveThreadCount() == 1 && currentProcess->getSignalThreadCount() > 0 && currentProcess->getWaitThreadCount() > 0)) {
		//if (currentProcess->getThreadCount() > 1) {
			DEBUG('z', "[INFO][Exit_syscall](C) Removing Thread %d from Process %d.\n", currentThread->threadID, currentProcess->getProcessID());
			//DEBUG('z', "[INFO][Exit_syscall](C) Removing Thread %d: %s from Process %d: %s.\n", currentThread->threadID, currentThread->getName(), currentProcess->getProcessID(), currentProcess->getName());
			currentProcess->removeThread(currentThread->threadID);
			currentThread->space->destroyStack(currentThread->threadID);
			syscallLock->Release();
			//processTableLock->Release();
			currentThread->Finish();
		}
		// if it's the last thread in the process, we have to clearn up the data
		else {
			//if there are other threads in the process that are stuck, then what do we do?
			//nothing to worry, since when deleteing the AddrSpace, it will check all the valid memory spaces and delete
			if (currentProcess->getThreadCount() > 1) {
				DEBUG('z', "[INFO][Exit_syscall] There are threads in BLOCKED status for Process %d, but process is going to be removed.\n", currentProcess->getProcessID());
				//DEBUG('z', "[INFO][Exit_syscall] There are threads in BLOCKED status for Process %d: %s, but process is going to be removed.\n", currentProcess->getProcessID(), currentProcess->getName());
				printf("[INFO][Exit_syscall] There are threads in BLOCKED status for Process %d, but the process is going to be removed.\n", currentProcess->getProcessID());
			}

			DEBUG('z', "[INFO][Exit_syscall](D) Removing Process %d with Thread %d.\n", currentProcess->getProcessID(), currentThread->threadID);
			//DEBUG('z', "[INFO][Exit_syscall](D) Removing Process %d: %s with Thread %d: %s.\n", currentProcess->getProcessID(), currentProcess->getName(), currentThread->threadID, currentThread->getName());
			currentProcess->removeThread(currentThread->threadID);
			currentThread->space->destroyStack(currentThread->threadID);
			processTable->removeProcess(currentProcess->getProcessID());
			DEBUG('z', "[INFO][Exit_syscall](D) # of processes left: %d.\n", processTable->getProcessCount());

			//kernelLocksConditionsLock->Acquire();
			//kernelConditionsLock->Acquire();
			for (int i = 0; i < MAX_CONDITIONS; i++) {
				if (!kernelConditionsMap->Test(i)) {
					continue;
				}

				if (kernelConditions[i].addrSpace == currentSpace && kernelConditions[i].isToBeDeleted == TRUE) {
					if (kernelConditions[i].condition->isBusy()) {
						DEBUG('e', "[CAUTION][Exit_syscall] Condition ID [%d] is being used in Process %d.\n", i, currentProcess->getProcessID());
					}
					DEBUG('e', "[INFO][Exit_syscall] Condition ID [%d] is marked to be deleted, so deleting for Process %d.\n", i, currentProcess->getProcessID());
					delete kernelConditions[i].condition;
					kernelConditions[i].condition = NULL;
					kernelConditions[i].addrSpace = NULL;
					kernelConditions[i].isToBeDeleted = FALSE;
				}
			}	
			//kernelConditionsLock->Release();

			//kernelLocksLock->Acquire();
			for (int i = 0; i < MAX_LOCKS; i++) {
				if (!kernelLocksMap->Test(i)) {
					continue;
				}

				if (kernelLocks[i].addrSpace == currentSpace && kernelLocks[i].isToBeDeleted == TRUE) {
					if (kernelLocks[i].lock->isBusy()) {
						DEBUG('e', "[CAUTION][Exit_syscall] Lock ID [%d] is being used in Process %d.\n", i, currentProcess->getProcessID());
					}
					DEBUG('e', "[INFO][Exit_syscall] Lock ID [%d] is marked to be deleted, so deleting for Process %d.\n", i, currentProcess->getProcessID());
					delete kernelLocks[i].lock;
					kernelLocks[i].lock = NULL;
					kernelLocks[i].addrSpace = NULL;
					kernelLocks[i].isToBeDeleted = FALSE;
				}
			}
			//kernelLocksLock->Release();
			//kernelLocksConditionsLock->Release();

			delete currentSpace;
			delete currentProcess;
			//printf("WE ARE HERE...\n");
			//syscallLock->Release();
			//processTableLock->Release();

			//currentThread->Finish();
			interrupt->Halt();
		}
	}
}

/*
    * Initialize the register by using currentThread->space.
    * Call Restore State through currentThread->space.
    * Call machine->Run()
 */
void exec_thread() {
	DEBUG('s', "Process [%d]: [%s] is executing with Thread [%d]: [%s]\n", currentThread->process->getProcessID(), currentThread->process->getName(), currentThread->threadID, currentThread->getName());
	execLock->Acquire();

	currentThread->space->InitRegisters();
	currentThread->space->RestoreState();
	
	execLock->Release();
	
	machine->Run();
	ASSERT(FALSE);
}

/*
    * Read the virtual address of the name of the process from the register R4 virtualAddress = machine->ReadRegister(4).
    * Convert it to the physical address and read the contents from there , which will give you the name of the process to be executed.
    * Now Open that file using filesystem->Open.
    * Store its openfile pointer.
    * Create new addrespace for this executable file.
    * Create a new thread.
    * Allocate the space created to this thread's space.
    * Update the process table and related data structures.
    * Write the space ID to the register 2.
    * Fork the new thread. I call it exec_thread.
 */
SpaceId Exec_syscall(unsigned int vaddr, int len) {
	char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file

	char* name;
	AddrSpace* space = NULL;
	Thread* thread = NULL;
	Process* process = NULL;
	int processCount = -1;

	syscallLock->Acquire();
	//processTableLock->Acquire();

    if (!buf) {
		//DEBUG('s', "[ERROR][Exec_syscall] Can't allocate kernel buffer in Open\n");
		printf("[ERROR][Exec_syscall] Can't allocate kernel buffer in Open\n");
		syscallLock->Release();
		//processTableLock->Release();
		return -1;
    }

    if( copyin(vaddr,len,buf) == -1 ) {
		//DEBUG('s', "[ERROR][Exec_syscall] Bad pointer passed to Open\n");
		printf("[ERROR][Exec_syscall] Bad pointer passed to Open\n");
		delete[] buf;
		syscallLock->Release();
		//processTableLock->Release();
		return -1;
    }

    buf[len]='\0';

    f = fileSystem->Open(buf);
	
	// the file is valid, now we will load the file into AddrSpace and create process/thread for it.
    if ( f ) {
		space = new AddrSpace(f);

		if (space == NULL) {
			//DEBUG('s', "[ERROR][Exec_syscall] Unable to create address space for file %s\n", buf);
			printf("[ERROR][Exec_syscall] Unable to create address space for file %s\n", buf);
			delete[] buf;
			syscallLock->Release();
			//processTableLock->Release();
			return -1;
		}

		if (!space->validProgram) {
			//DEBUG('s', "[ERROR][Exec_syscall] Unable to load program onto address space for file %s\n", buf);
			printf("[ERROR][Exec_syscall] Unable to load program onto address space for file %s\n", buf);
			delete[] buf;
			delete space;
			syscallLock->Release();
			//processTableLock->Release();
			return -1;
		}

		if (!space->validPageTable) {
			//DEBUG('s', "[ERROR][Exec_syscall] Unable to allocate enough memory for file %s\n", buf);
			printf("[ERROR][Exec_syscall] Unable to allocate enough memeory for file %s\n", buf);
			delete[] buf;
			delete space;
			syscallLock->Release();
			//processTableLock->Release();
			return -1;
		}

		// finish allocating the memory space for the user program, now create a process/main thread
		name = new char[20];
		processCount = processTable->getProcessCount();
		sprintf(name, "process %d", processCount);
		process = processTable->createProcess(name, space);

		if (process == NULL) {
			//DEBUG('s', "[ERROR][Exec_syscall] Unable to create process for file %s\n", buf);
			printf("[ERROR][Exec_syscall] Unable to create process for file %s\n", buf);
			delete[] buf;
			delete space;
			syscallLock->Release();
			//processTableLock->Release();
			return -1;
		}

		name = new char[30];
		sprintf(name, "main for process %d", processCount);
		thread = process->createThread(name);

		// if unable to create main thread for the new user program/process, something is totally wrong, stop NACHOS
		if (thread == NULL) {
			//DEBUG('s', "[ERROR][Exec_syscall] Unable to create main thread for process %d\n", process->getProcessID());
			printf("[ERROR][Exec_syscall] Unable to create main for process %d\n", process->getProcessID());
			interrupt->Halt();
		}

		DEBUG('s', "Process [%d] is created with Thread [%d]\n", process->getProcessID(), thread->threadID);
		DEBUG('z', "Total processes: %d, Process %d with Thread: %d, Active Thread: %d\n", processTable->getProcessCount(), process->getProcessID(), process->getThreadCount(), process->getActiveThreadCount());
		//DEBUG('s', "Process [%d]: [%s] is created with Thread [%d]: [%s]\n", process->getProcessID(), process->getName(), thread->threadID, thread->getName());

		thread->Fork((VoidFunctionPtr)exec_thread, 0);

		syscallLock->Release();
		//processTableLock->Release();

		return process->getProcessID();
    }
    else {
		//DEBUG('s', "[ERROR][Exec_syscall] Unable to Open bad file %s\n", buf);
		printf("[ERROR][Exec_syscall] Unable to Open bad file %s\n", buf);
		delete[] buf;
		syscallLock->Release();
		//processTableLock->Release();
		return -1;
	}
}

/*
This would be a function like void kernel_thread(int virtualaddress) and here is how you can implement this function.

    * write to the register PCReg the virtual address.
    * write virtualaddress + 4 in NextPCReg.
    * call Restorestate function inorder to prevent information loss while context switching.
    * write to the stack register , the starting postion of the stack for this thread.
    * call machine->Run()
 */
void kernel_thread(unsigned int vaddr) {
	DEBUG('s', "Process [%d]: [%s] is forking with Thread [%d]: [%s]\n", currentThread->process->getProcessID(), currentThread->process->getName(), currentThread->threadID, currentThread->getName());
	forkLock->Acquire();

	machine->WriteRegister(PCReg, vaddr);
	machine->WriteRegister(NextPCReg, (vaddr+4));

	currentThread->space->RestoreState();

	machine->WriteRegister(StackReg, currentThread->stackPointer);
	
	forkLock->Release();
	machine->Run(); 
	ASSERT(FALSE);
}

/*
    * Read the virtual address of user function from Register R4 virtualAddress = machine->ReadRegister(4)
    * Create a New thread. This would be a kernel thread.
    * Update the Process Table for Multiprogramming part.
    * Allocate the addrespace to the thread being forked which is essentially current thread's addresspsace because threads share the process addressspace.
 */
ForkId Fork_syscall(unsigned int vaddr) {
	char* name;
	Thread* thread;

	syscallLock->Acquire();
	//processTableLock->Acquire();

	// check to see if the virtual address is outsize the range of the program range
	if (vaddr == 0 || vaddr >= currentThread->space->getCodeSize()) {
		//DEBUG('s', "[ERROR][Fork_syscall] User function virtual address is invalid for process %s\n", currentThread->process->getName());
		printf("[ERROR][Fork_syscall] User function virtual address is invalid for process %s\n", currentThread->process->getName());
		syscallLock->Release();
		//processTableLock->Release();
		return -1;
	}

	name = new char[30];
	sprintf(name, "thread for process %d", currentThread->process->getProcessID());
	thread = currentThread->process->createThread(name);

	if (thread == NULL) {
		//DEBUG('z', "[ERROR][Fork_syscall] Unable to create thread for process %s\n", currentThread->process->getName());
		printf("[ERROR][Fork_syscall] Unable to create thread for process %s\n", currentThread->process->getName());
		syscallLock->Release();
		//processTableLock->Release();
		return -1;
	}
	else {
		//allocate stack space for the thread, if not successful, do not fork the thread.
		if (!currentThread->space->createStack(thread->threadID)) {
			currentThread->process->removeThread(thread->threadID);
			delete thread;

			//DEBUG('s', "[ERROR][Fork_syscall] Unable to allocate thread stack space for process %s\n", currentThread->process->getName());
			printf("[ERROR][Fork_syscall] Unable to allocate thread stack space for process %s\n", currentThread->process->getName());
			syscallLock->Release();
			//processTableLock->Release();
			return -1;
		}
		else {
			thread->stackPointer = currentThread->space->getStackPointer(thread->threadID);
			//DEBUG('z', "Process [%s] with Main Thread [%s] and Thread Stack Pointer [%d] or [%x]\n", thread->process->getName(), thread->getName(), thread->stackPointer, thread->stackPointer);
		}
	}

	DEBUG('s', "Process [%d]: [%s] is forked with Thread [%d]: [%s]\n", thread->process->getProcessID(), thread->process->getName(), thread->threadID, thread->getName());
	thread->Fork((VoidFunctionPtr)kernel_thread,vaddr);

	syscallLock->Release();
	//processTableLock->Release();

	return thread->threadID;
}

void Yield_syscall() {
	currentThread->Yield();
}

#ifdef NETWORK 
int CreateLock_syscall(unsigned int vaddr, int len){

	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[20];
	char lockname[10];
	char char_response[MaxMailSize];
	int response;

	if(len>10){
		printf("error: lock name is too long.\n");
		return -1;
	}
		
	syscallLock->Acquire();
	copyin(vaddr,len,lockname);
	lockname[len]='\0';
	printf("LOCKNAME: %s\n",lockname);

	sprintf(request,"%s%s","LC",lockname);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;

	bool success = postOffice->Send(outPktHdr, outMailHdr, request);

	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");

	//printf("threadid: %d is waiting for the server.\n", currentThread->threadID);
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);
	response = atoi(char_response);
	
	syscallLock->Release();
	return response;

}

void DestroyLock_syscall(int lockID){
	
	syscallLock->Acquire();
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%d","LD",lockID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	//send request
	bool success = postOffice->Send(outPktHdr, outMailHdr, request);
	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	if(char_response[0]=='F'){
		switch(char_response[1]){
			case '1':
			printf("destroy error 1: lock is not existent.\n");
			break;
			case '2':
			printf("destroy error 2: lock is busy\n");
			break;
			case '3':
			printf("destroy error 3: lock belongs to others.");
			break;
		}
		
	}else if(char_response[0]=='S'){
		//printf("destroy success.\n");
	}
	
	syscallLock->Release();	
	
	return ;
}

void Acquire_syscall(int lockID){

	
	syscallLock->Acquire();
	//printf("%d is going to acquire lock %d\n", currentThread->threadID,lockID);
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%d","A",lockID);
	
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, request);
	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");

	syscallLock->Release();

	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);
	//printf("%d acquired lock %d\n", currentThread->threadID,lockID);
	
	syscallLock->Acquire();

	
	if(char_response[0]=='F'){
		switch(char_response[1]){
			case '4':
			printf("Acquire error 1: lock is not existent.\n");
			break;
			case '5':
			printf("Acquire  error 2: lock belongs to others\n");
			break;
		}		
	}else if(char_response[0]=='S'){
		//printf("Acquire success.\n");
	}

	
	syscallLock->Release();	

}

void Release_syscall(int lockID){
	syscallLock->Acquire();
	
	//printf("%d is going to release lock %d\n", currentThread->threadID,lockID);

	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%d","R",lockID);
	
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;

	bool success = postOffice->Send(outPktHdr, outMailHdr, request);
	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");

	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	if(char_response[0]=='F'){
		switch(char_response[1]){
			case '4':
			printf("Release error 1: lock is not existent.\n");
			break;
			case '6':
			printf("Release  error 2: lock belongs to other host\n");
			break;
			case '7':
			printf("Release  error 3: lock belongs to other thread\n");
		}		
	}else if(char_response[0]=='S'){
		//printf("Release success.\n");
	}

	//printf("%d is released lock %d\n", currentThread->threadID,lockID);
	syscallLock->Release(); 
}

int CreateCV_syscall(unsigned int vaddr, int len){
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[20];
	char char_response[MaxMailSize];
	char name[10];
	int response;


	syscallLock->Acquire();
	if(len>10){
		printf("error: lock name is too long.\n");
		syscallLock->Release();
		return -1;
	}

	copyin(vaddr,len,name);
	name[len]='\0';
	printf("CVNAME: %s\n", name);
	sprintf(request,"%s%s","CC",name);
	
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;

	bool success = postOffice->Send(outPktHdr, outMailHdr, request);

	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");

	//printf("threadid: %d is waiting for the server.\n", currentThread->threadID);
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);
	response = atoi(char_response);
	
	syscallLock->Release();
	return response;

}

void DestroyCV_syscall(int cvID){
	
	syscallLock->Acquire();
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%d","CD",cvID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	//send request
	bool success = postOffice->Send(outPktHdr, outMailHdr, request);
	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	if(char_response[0]=='F'){
		switch(char_response[1]){
			case '8':
			printf("destroy error 1: cv is not existent.\n");
			break;
			case '9':
			printf("destroy error 2: cv ibelongs to others.\n");
			break;
		}
		
	}else if(char_response[0]=='S'){
		//printf("destroy cv success.\n");
	}
	
	syscallLock->Release();	
	
	return ;
}

void Wait_syscall(int cvID, int lockID){
	
	syscallLock->Acquire();
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;
	
	
	sprintf(request, "%s%-3d%s%-3d","T",cvID,"_",lockID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	
	postOffice->Send(outPktHdr, outMailHdr, request);
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	if(char_response[0]=='F'){
		if(char_response[1]=='8')
			printf("wait error 1: cv is inexistent.\n");
		else if(char_response[1]=='A')
			printf("wait error 2: does not own the lock.\n");

		syscallLock->Release();
		return;
		}

	sprintf(request, "%s%-3d%s%-3d","W",cvID,"_",lockID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	
	postOffice->Send(outPktHdr, outMailHdr, request);

	syscallLock->Release();
	
	Release_syscall(lockID);
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	Acquire_syscall(lockID);

	if(char_response[0]=='F'){
		switch(char_response[1]){
			case 'A':
			printf("wait error 1: does not own the lock.\n");
			break;
			case 'B':
			printf("wait error 2: lock is different.\n");
			break;
			case '8':
			printf("wait error 4: cv is inexistent.\n");
			break;
		}
	}		

	return;

}

void Signal_syscall(int cvID, int lockID){
	
	syscallLock->Acquire();
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%-3d%s%-3d","S",cvID,"_",lockID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	
	postOffice->Send(outPktHdr, outMailHdr, request);
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	
	if(char_response[0]=='F'){
		switch(char_response[1]){
			case 'A':
			printf("signal error 1: does not own the lock.\n");
			break;
			case 'B':
			printf("signal error 2: lock is different.\n");
			break;
			case 'C':
			printf("signal error 3: no thread is waiting.\n");
			break;
			case '8':
			printf("signal error 4: mv is inexistent.\n");
			break;
		}
		
	}else if(char_response[0]=='S'){
		printf("signal success.\n");
	}


	syscallLock->Release();
	
	return;

}

void Broadcast_syscall(int cvID, int lockID){
	
	syscallLock->Acquire();
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%-3d%s%-3d","B",cvID,"_",lockID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	
	postOffice->Send(outPktHdr, outMailHdr, request);
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	
	if(char_response[0]=='F'){
		switch(char_response[1]){
			case 'A':
			printf("broadcast error 1: does not own the lock.\n");
			break;
			case 'B':
			printf("broadcast error2: lock is different.\n");
			break;
			case 'C':
			printf("broadcast error3: no thread is waiting.\n");
			break;
			case '8':
			printf("broadcast error4: mv is inexistent.\n");
		}
		
	}else if(char_response[0]=='S'){
		printf("broadcatN success.\n");
	}

	syscallLock->Release();
	
	return;

}
/*
int CreateLockN_syscall(unsigned int vaddr, int len){

	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[20];
	char lockname[10];
	char char_response[MaxMailSize];
	int response;

	if(len>10){
		printf("error: lock name is too long.\n");
		return -1;
	}
		
	syscallLock->Acquire();
	copyin(vaddr,len,lockname);
	lockname[len]='\0';
	printf("LOCKNAME: %s\n",lockname);

	sprintf(request,"%s%s","LC",lockname);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;

	bool success = postOffice->Send(outPktHdr, outMailHdr, request);

	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");

	//printf("threadid: %d is waiting for the server.\n", currentThread->threadID);
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);
	response = atoi(char_response);
	
	syscallLock->Release();
	return response;

}

void DestroyLockN_syscall(int lockID){
	
	syscallLock->Acquire();
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%d","LD",lockID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	//send request
	bool success = postOffice->Send(outPktHdr, outMailHdr, request);
	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	if(char_response[0]=='F'){
		switch(char_response[1]){
			case '1':
			printf("destroy error 1: lock is not existent.\n");
			break;
			case '2':
			printf("destroy error 2: lock is busy\n");
			break;
			case '3':
			printf("destroy error 3: lock belongs to others.");
			break;
		}
		
	}else if(char_response[0]=='S'){
		//printf("destroy success.\n");
	}
	
	syscallLock->Release();	
	
	return ;
}

void AcquireN_syscall(int lockID){

	
	syscallLock->Acquire();
	//printf("%d is going to acquire lock %d\n", currentThread->threadID,lockID);
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%d","A",lockID);
	
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, request);
	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");

	syscallLock->Release();

	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);
	//printf("%d acquired lock %d\n", currentThread->threadID,lockID);
	
	syscallLock->Acquire();

	
	if(char_response[0]=='F'){
		switch(char_response[1]){
			case '4':
			printf("Acquire error 1: lock is not existent.\n");
			break;
			case '5':
			printf("Acquire  error 2: lock belongs to others\n");
			break;
		}		
	}else if(char_response[0]=='S'){
		//printf("Acquire success.\n");
	}

	
	syscallLock->Release();	

}

void ReleaseN_syscall(int lockID){
	syscallLock->Acquire();
	
	//printf("%d is going to release lock %d\n", currentThread->threadID,lockID);

	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%d","R",lockID);
	
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;

	bool success = postOffice->Send(outPktHdr, outMailHdr, request);
	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");

	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	if(char_response[0]=='F'){
		switch(char_response[1]){
			case '4':
			printf("Release error 1: lock is not existent.\n");
			break;
			case '6':
			printf("Release  error 2: lock belongs to other host\n");
			break;
			case '7':
			printf("Release  error 3: lock belongs to other thread\n");
		}		
	}else if(char_response[0]=='S'){
		//printf("Release success.\n");
	}

	//printf("%d is released lock %d\n", currentThread->threadID,lockID);
	syscallLock->Release(); 
}

int CreateCVN_syscall(unsigned int vaddr, int len){
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[20];
	char char_response[MaxMailSize];
	char name[10];
	int response;


	syscallLock->Acquire();
	if(len>10){
		printf("error: lock name is too long.\n");
		syscallLock->Release();
		return -1;
	}

	copyin(vaddr,len,name);
	name[len]='\0';
	printf("CVNAME: %s\n", name);
	sprintf(request,"%s%s","CC",name);
	
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;

	bool success = postOffice->Send(outPktHdr, outMailHdr, request);

	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");

	//printf("threadid: %d is waiting for the server.\n", currentThread->threadID);
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);
	response = atoi(char_response);
	
	syscallLock->Release();
	return response;

}

void DestroyCVN_syscall(int cvID){
	
	syscallLock->Acquire();
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%d","CD",cvID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	//send request
	bool success = postOffice->Send(outPktHdr, outMailHdr, request);
	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	if(char_response[0]=='F'){
		switch(char_response[1]){
			case '8':
			printf("destroy error 1: cv is not existent.\n");
			break;
			case '9':
			printf("destroy error 2: cv ibelongs to others.\n");
			break;
		}
		
	}else if(char_response[0]=='S'){
		//printf("destroy cv success.\n");
	}
	
	syscallLock->Release();	
	
	return ;
}

void waitN_syscall(int cvID, int lockID){
	
	syscallLock->Acquire();
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;
	
	
	sprintf(request, "%s%-3d%s%-3d","T",cvID,"_",lockID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	
	postOffice->Send(outPktHdr, outMailHdr, request);
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	if(char_response[0]=='F'){
		if(char_response[1]=='8')
			printf("wait error 1: cv is inexistent.\n");
		else if(char_response[1]=='A')
			printf("wait error 2: does not own the lock.\n");

		syscallLock->Release();
		return;
		}

	sprintf(request, "%s%-3d%s%-3d","W",cvID,"_",lockID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	
	postOffice->Send(outPktHdr, outMailHdr, request);

	syscallLock->Release();
	
	ReleaseN_syscall(lockID);
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	AcquireN_syscall(lockID);

	if(char_response[0]=='F'){
		switch(char_response[1]){
			case 'A':
			printf("wait error 1: does not own the lock.\n");
			break;
			case 'B':
			printf("wait error 2: lock is different.\n");
			break;
			case '8':
			printf("wait error 4: cv is inexistent.\n");
			break;
		}
	}		

	return;

}

void signalN_syscall(int cvID, int lockID){
	
	syscallLock->Acquire();
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%-3d%s%-3d","S",cvID,"_",lockID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	
	postOffice->Send(outPktHdr, outMailHdr, request);
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	
	if(char_response[0]=='F'){
		switch(char_response[1]){
			case 'A':
			printf("signal error 1: does not own the lock.\n");
			break;
			case 'B':
			printf("signal error 2: lock is different.\n");
			break;
			case 'C':
			printf("signal error 3: no thread is waiting.\n");
			break;
			case '8':
			printf("signal error 4: mv is inexistent.\n");
			break;
		}
		
	}else if(char_response[0]=='S'){
		printf("signal success.\n");
	}


	syscallLock->Release();
	
	return;

}

void broadcastN_syscall(int cvID, int lockID){
	
	syscallLock->Acquire();
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%-3d%s%-3d","B",cvID,"_",lockID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	
	postOffice->Send(outPktHdr, outMailHdr, request);
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	
	if(char_response[0]=='F'){
		switch(char_response[1]){
			case 'A':
			printf("broadcast error 1: does not own the lock.\n");
			break;
			case 'B':
			printf("broadcast error2: lock is different.\n");
			break;
			case 'C':
			printf("broadcast error3: no thread is waiting.\n");
			break;
			case '8':
			printf("broadcast error4: mv is inexistent.\n");
		}
		
	}else if(char_response[0]=='S'){
		printf("broadcatN success.\n");
	}

	syscallLock->Release();
	
	return;

}

*/
int CreateMVN_syscall(unsigned int vaddr, int len, int arraysize){
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[20];
	char char_response[MaxMailSize];
	char name[10];
	int response;


	syscallLock->Acquire();
	if(len>10){
		printf("error: lock name is too long.\n");
		syscallLock->Release();
		return -1;
	}

	copyin(vaddr,len,name);
	name[len]='\0';
	printf("MVNAME: %s\n", name);
	sprintf(request,"%s%-10s%d","MC",name,arraysize);
	
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;

	bool success = postOffice->Send(outPktHdr, outMailHdr, request);

	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");

	//printf("threadid: %d is waiting for the server.\n", currentThread->threadID);
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);
	response = atoi(char_response);
	
	syscallLock->Release();
	return response;

}

void DestroyMVN_syscall(int mvID){
	
	syscallLock->Acquire();
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[10];
	char data[MaxMailSize];
	char char_response[MaxMailSize];
	int response;

	sprintf(request, "%s%d","MD",mvID);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;
	//send request
	bool success = postOffice->Send(outPktHdr, outMailHdr, request);
	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	if(char_response[0]=='F'){
		switch(char_response[1]){
			case '8':
			printf("destroy error 1: mv is not existent.\n");
			break;
		}
		
	}else if(char_response[0]=='S'){
		//printf("destroy mv success.\n");
	}
	
	syscallLock->Release();	
	
	return ;
}

void SetMV_syscall(int mvid, int index, int value){
	syscallLock->Acquire();
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[20];
	char char_response[10];

	sprintf(request, "%s%-3d%-3d%-5d", "MS",mvid,index,value);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;

	bool success = postOffice->Send(outPktHdr, outMailHdr, request);

	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	if(char_response[0]=='F'){
		switch(char_response[1]){
			case '8':
			printf("setmv error 1: mv is not existent.\n");
			break;
		}
		
	}else if(char_response[0]=='S'){
		printf("set mv success.\n");
	}
	
	syscallLock->Release();

	return;

}

int GetMV_syscall(int mvid,int index){
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[20];
	char char_response[10];
	int value;

	syscallLock->Acquire();

	sprintf(request, "%s%-3d%-3d", "MG",mvid,index);
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;

	bool success = postOffice->Send(outPktHdr, outMailHdr, request);

	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	if(char_response[0]=='F'){
		switch(char_response[1]){
			case '8':
			printf("Get MV error 1: mv is not existent.\n");
			break;
		}	
	}else{
		printf("Get MV success.\n");
		value = atoi(char_response);
	}
	
	syscallLock->Release();

	return value;

}

void stopServer_syscall(){
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	char request[20];
	//char char_response[10];
	//int value;

	syscallLock->Acquire();

	sprintf(request,"%s","K");
	outPktHdr.to = 0;
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->threadID;
	outMailHdr.length = strlen(request)+1;

	bool success = postOffice->Send(outPktHdr, outMailHdr, request);

	if(!success)
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
/*	
	postOffice->Receive(currentThread->threadID,&inPktHdr,&inMailHdr,char_response);

	if(char_response[0]=='F'){
		switch(char_response[1]){
			case '8':
			printf("Get MV error 1: mv is not existent.\n");
			break;
		}	
	}else{
		printf("Get MV success.\n");
		value = atoi(char_response);
	}
*/	
	syscallLock->Release();

	return;

}

#else
int CreateLock_syscall() {
	int lockID = -1;
	char* name;

	syscallLock->Acquire();
	

	// Try to find a space in KernelLocks array and see if it's not being used.
	lockID = kernelLocksMap->Find();
	// No more space to allocate Locks, print the error message.
	if (lockID < 0) {
		
		printf("[ERROR][CreateLock_syscall] Unable to create new Lock.\n");
	}
	else {
		DEBUG('s', "[INFO][CreateLock_syscall] Lock ID [%d] is being created.\n", lockID);
		name = new char[30];
		sprintf(name, "Kernel Lock [%d]", lockID);
		kernelLocks[lockID].lock = new Lock(name);
		kernelLocks[lockID].addrSpace = currentThread->space;
		kernelLocks[lockID].isToBeDeleted = FALSE;
	}

	syscallLock->Release();

	return lockID;
}

void DestroyLock_syscall(int lockID) {
	// if the lock ID is not between 0 and MAX_LOCKS-1, it's invalid
	if (lockID < 0 || lockID >= MAX_LOCKS) {
		//DEBUG('s', "[ERROR][DestroyLock_syscall] Lock ID [%d] is invalid to destroy.\n", lockID);
		printf("[ERROR][DestroyLock_syscall] Lock ID [%d] is invalid to destroy.\n", lockID);
		return;
	}
	else {
		syscallLock->Acquire();
		//kernelLocksLock->Acquire();
		// if the lock wasn't allocated, then it's not a valid lock to delete
		if (!kernelLocksMap->Test(lockID)) {
			//DEBUG('s', "[ERROR][DestroyLock_syscall] No Lock ID [%d] was allocated to destroy.\n", lockID);
			printf("[ERROR][DestroyLock_syscall] No Lock ID [%d] was allocated to destroy.\n", lockID);
		}
		else {
			// if the lock isn't associated with the current thread/process space, wrong owner
			if (kernelLocks[lockID].addrSpace != currentThread->space) {
				DEBUG('s', "[ERROR][DestroyLock_syscall] Lock ID [%d] isn't associated with current thread/process to destroy.\n", lockID);
				printf("[ERROR][DestroyLock_syscall] Lock ID [%d] isn't associated with current thread/process to destroy.\n", lockID);
			}
			// everything checks out, now we can delete the lock
			else {
				if (kernelLocks[lockID].lock == NULL) {
					//DEBUG('s', "[ERROR][DestroyLock_syscall] HOW CAN Lock ID [%d] BE ALLOCATED BUT IT'S NULL!\n", lockID);
					printf("[ERROR][DestroyLock_syscall] HOW CAN Lock ID [%d] BE ALLOCATED BUT IT'S NULL!\n", lockID);
				}
				else if (kernelLocks[lockID].lock->isBusy()) {
					//DEBUG('s', "[INFO][DestroyLock_syscall] Lock ID [%d] is being used. Is marked to be deleted.\n", lockID);
					printf("[INFO][DestroyLock_syscall] Lock ID [%d] is being used. Is marked to be deleted.\n", lockID);
					kernelLocks[lockID].isToBeDeleted = TRUE;
				}
				else {
					DEBUG('s', "[INFO][DestroyLock_syscall] Lock ID [%d] is being deleted.\n", lockID);
					delete kernelLocks[lockID].lock;
					kernelLocks[lockID].lock = NULL;
					kernelLocks[lockID].addrSpace = NULL;
					kernelLocks[lockID].isToBeDeleted = FALSE;
					kernelLocksMap->Clear(lockID);
					//DEBUG('s', "[INFO] # of Locks available = [%d]\n", kernelLocksMap->NumClear());
				}				
			}
		}
		syscallLock->Release();
		//kernelLocksLock->Release();
	}
}

void Acquire_syscall(int lockID) {
	// if the lock ID is not between 0 and MAX_LOCKS-1, it's invalid
	if (lockID < 0 || lockID >= MAX_LOCKS) {
		//DEBUG('s', "[ERROR][Acquire_syscall] Lock ID [%d] is invalid to acquire.\n", lockID);
		printf("[ERROR][Acquire_syscall] Lock ID [%d] is invalid to acquire.\n", lockID);
		return;
	}
	else {
		syscallLock->Acquire();
		//kernelLocksConditionsLock->Acquire();
		//kernelLocksLock->Acquire();
		// if the lock wasn't allocated, then it's not a valid lock to delete
		if (!kernelLocksMap->Test(lockID)) {
			//DEBUG('s', "[ERROR][Acquire_syscall] No Lock ID [%d] was allocated to acquire.\n", lockID);
			printf("[ERROR][Acquire_syscall] No Lock ID [%d] was allocated to acquire.\n", lockID);
			syscallLock->Release();
			//kernelLocksConditionsLock->Release();
			//kernelLocksLock->Release();
		}
		else {
			// if the lock isn't associated with the current thread/process space, wrong owner
			if (kernelLocks[lockID].addrSpace != currentThread->space) {
				//DEBUG('s', "[ERROR][DestroyLock_syscall] Lock ID [%d] isn't associated with current thread/process to acquire.\n", lockID);
				printf("[ERROR][DestroyLock_syscall] Lock ID [%d] isn't associated with current thread/process to acquire.\n", lockID);
				syscallLock->Release();
				//kernelLocksConditionsLock->Release();
				//kernelLocksLock->Release();
			}
			// everything checks out, now we can acquire the lock
			else {
				DEBUG('s', "[INFO][Acquire_syscall] Lock ID [%d] is being acquired.\n", lockID);
				//currentThread->process->decreaseActiveThreadCount();
				syscallLock->Release();
				//kernelLocksConditionsLock->Release();
				//kernelLocksLock->Release();
				kernelLocks[lockID].lock->Acquire();
				//syscallLock->Acquire();
				//currentThread->process->increaseActiveThreadCount();
				//syscallLock->Release();
			}
		}
	}
}

void Release_syscall(int lockID) {
	// if the lock ID is not between 0 and MAX_LOCKS-1, it's invalid
	if (lockID < 0 || lockID >= MAX_LOCKS) {
		//DEBUG('s', "[ERROR][Release_syscall] Lock ID [%d] is invalid to release.\n", lockID);
		printf("[ERROR][Release_syscall] Lock ID [%d] is invalid to release.\n", lockID);
		return;
	}
	else {
		syscallLock->Acquire();
		//kernelLocksConditionsLock->Acquire();
		//kernelLocksLock->Acquire();
		// if the lock wasn't allocated, then it's not a valid lock to delete
		if (!kernelLocksMap->Test(lockID)) {
			//DEBUG('s', "[ERROR][Release_syscall] No Lock ID [%d] was allocated to release.\n", lockID);
			printf("[ERROR][Release_syscall] No Lock ID [%d] was allocated to release.\n", lockID);
			syscallLock->Release();
			//kernelLocksConditionsLock->Release();
			//kernelLocksLock->Release();
		}
		else {
			// if the lock isn't associated with the current thread/process space, wrong owner
			if (kernelLocks[lockID].addrSpace != currentThread->space) {
				//DEBUG('s', "[ERROR][Release_syscall] Lock ID [%d] isn't associated with current thread/process to release.\n", lockID);
				printf("[ERROR][Release_syscall] Lock ID [%d] isn't associated with current thread/process to release.\n", lockID);
				syscallLock->Release();
				//kernelLocksConditionsLock->Release();
				//kernelLocksLock->Release();
			}
			// everything checks out, now we can acquire the lock
			else {
				DEBUG('s', "[INFO][Release_syscall] Lock ID [%d] is being released.\n", lockID);
				//kernelLocksConditionsLock->Release();
				//kernelLocksLock->Release();
				kernelLocks[lockID].lock->Release();
				syscallLock->Release();
			}
		}
	}
}

int CreateCV_syscall() {
	int conditionID = -1;
	char* name;

	syscallLock->Acquire();
	//kernelLocksConditionsLock->Acquire();
	//kernelConditionsLock->Acquire();

	// Try to find a space in KernelConditions array and see if it's not being used.
	conditionID = kernelConditionsMap->Find();
	// No more space to allocate Conditions, print the error message.
	if (conditionID < 0) {
		//DEBUG('s', "[ERROR][CreateCondition_syscall] Unable to create new Condition Variable.\n");
		printf("[ERROR][CreateCondition_syscall] Unable to create new Condition Variable.\n");
	}
	else {
		DEBUG('s', "[INFO][CreateCondition_syscall] Condition ID [%d] is being created.\n", conditionID);
		name = new char[30];
		sprintf(name, "Kernel Condition [%d]", conditionID);
		kernelConditions[conditionID].condition = new Condition("KernelCondition");
		kernelConditions[conditionID].addrSpace = currentThread->space;
		kernelConditions[conditionID].isToBeDeleted = FALSE;
	}

	syscallLock->Release();
	//kernelLocksConditionsLock->Release();
	//kernelConditionsLock->Release();

	return conditionID;
}

void DestroyCV_syscall(int conditionID) {
	// if the condition ID is not between 0 and MAX_CONDITIONS-1, it's invalid
	if (conditionID < 0 || conditionID >= MAX_CONDITIONS) {
		//DEBUG('s', "[ERROR][DestroyCondition_syscall] Condition ID [%d] is invalid to destroy.\n", conditionID);
		printf("[ERROR][DestroyCondition_syscall] Condition ID [%d] is invalid to destroy.\n", conditionID);
		return;
	}
	else {
		syscallLock->Acquire();
		//kernelLocksConditionsLock->Acquire();
		//kernelConditionsLock->Acquire();
		// if the lock wasn't allocated, then it's not a valid lock to delete
		if (!kernelConditionsMap->Test(conditionID)) {
			//DEBUG('s', "[ERROR][DestroyCondition_syscall] No Condition ID [%d] was allocated to destroy.\n", conditionID);
			printf("[ERROR][DestroyCondition_syscall] No Condition ID [%d] was allocated to destroy.\n", conditionID);
		}
		else {
			// if the condition isn't associated with the current thread/process space, wrong owner
			if (kernelConditions[conditionID].addrSpace != currentThread->space) {
				//DEBUG('s', "[ERROR][DestroyCondition_syscall] Condition ID [%d] isn't associated with current thread/process to destroy.\n", conditionID);
				printf("[ERROR][DestroyCondition_syscall] Condition ID [%d] isn't associated with current thread/process to destroy.\n", conditionID);
			}
			// everything checks out, now we can delete the condition
			else {
				if (kernelConditions[conditionID].condition == NULL) {
					//DEBUG('s', "[ERROR][DestroyLock_syscall] HOW CAN Condition ID [%d] BE ALLOCATED BUT IT'S NULL!\n", conditionID);
					printf("[ERROR][DestroyLock_syscall] HOW CAN Condition ID [%d] BE ALLOCATED BUT IT'S NULL!\n", conditionID);
				}
				else if (kernelConditions[conditionID].condition->isBusy()) {
					//DEBUG('s', "[INFO][DestroyCondition_syscall] Condition ID [%d] is being used. Is marked to be deleted.\n", conditionID);
					printf("[INFO][DestroyCondition_syscall] Condition ID [%d] is being used. Is marked to be deleted.\n", conditionID);
					kernelConditions[conditionID].isToBeDeleted = TRUE;
				}
				else {
					DEBUG('s', "[INFO][DestroyCondition_syscall] Condition ID [%d] is being deleted.\n", conditionID);
					delete kernelConditions[conditionID].condition;
					kernelConditions[conditionID].condition = NULL;
					kernelConditions[conditionID].addrSpace = NULL;
					kernelConditions[conditionID].isToBeDeleted = FALSE;
					kernelConditionsMap->Clear(conditionID);
				}
			}
		}
		syscallLock->Release();
		//kernelLocksConditionsLock->Release();
		//kernelConditionsLock->Release();
	}
}

void Wait_syscall(int conditionID, int lockID) {
	// if the lock ID is not between 0 and MAX_LOCKS-1, it's invalid
	if (lockID < 0 || lockID >= MAX_LOCKS) {
		//DEBUG('s', "[ERROR][Wait_syscall] Lock ID [%d] is invalid to wait.\n", lockID);
		printf("[ERROR][Wait_syscall] Lock ID [%d] is invalid to wait.\n", lockID);
		return;
	}
	// if the condition ID is not between 0 and MAX_CONDITIONS-1, it's invalid
	if (conditionID < 0 || conditionID >= MAX_CONDITIONS) {
		//DEBUG('s', "[ERROR][Wait_syscall] Condition ID [%d] is invalid to wait.\n", conditionID);
		printf("[ERROR][Wait_syscall] Condition ID [%d] is invalid to wait.\n", conditionID);
		return;
	}

	// now we do a thorough check against the condition ID and lock ID, lock ID first
	syscallLock->Acquire();
	//kernelLocksConditionsLock->Acquire();
	//kernelLocksLock->Acquire();

	if (!kernelLocksMap->Test(lockID)) {
		//DEBUG('s', "[ERROR][Wait_syscall] No Lock ID [%d] was allocated to wait.\n", lockID);
		printf("[ERROR][Wait_syscall] No Lock ID [%d] was allocated to wait.\n", lockID);
		syscallLock->Release();
		//kernelLocksConditionsLock->Release();
		//kernelLocksLock->Release();
		return;
	}
	else if (kernelLocks[lockID].addrSpace != currentThread->space) {
		//DEBUG('s', "[ERROR][Wait_syscall] Lock ID [%d] isn't associated with current thread/process to wait.\n", lockID);
		printf("[ERROR][Wait_syscall] Lock ID [%d] isn't associated with current thread/process to wait.\n", lockID);
		syscallLock->Release();
		//kernelLocksConditionsLock->Release();
		//kernelLocksLock->Release();
		return;
	}
	else {
		//kernelConditionsLock->Acquire();
		
		if (!kernelConditionsMap->Test(conditionID)) {
			//DEBUG('s', "[ERROR][Wait_syscall] No Condition ID [%d] was allocated to wait.\n", conditionID);
			printf("[ERROR][Wait_syscall] No Condition ID [%d] was allocated to wait.\n", conditionID);
			syscallLock->Release();
			//kernelLocksConditionsLock->Release();
			//kernelConditionsLock->Release();
			//kernelLocksLock->Release();
			return;
		}
		else {
			// if the condition isn't associated with the current thread/process space, wrong owner
			if (kernelConditions[conditionID].addrSpace != currentThread->space) {
				//DEBUG('s', "[ERROR][Wait_syscall] Condition ID [%d] isn't associated with current thread/process to wait.\n", conditionID);
				printf("[ERROR][Wait_syscall] Condition ID [%d] isn't associated with current thread/process to wait.\n", conditionID);
				syscallLock->Release();
				//kernelLocksConditionsLock->Release();
				//kernelConditionsLock->Release();
				//kernelLocksLock->Release();
				return;
			}
			// everything checks out, now we can wait the condition
			else {
				DEBUG('s', "[INFO][Wait_syscall] Condition ID [%d] is waiting with Lock ID [%d].\n", conditionID, lockID);
				//currentThread->process->decreaseActiveThreadCount();
				syscallLock->Release();
				//kernelLocksConditionsLock->Release();
				//kernelConditionsLock->Release();
				//kernelLocksLock->Release();
				kernelConditions[conditionID].condition->Wait(kernelLocks[lockID].lock);
				//syscallLock->Acquire();
				//currentThread->process->increaseActiveThreadCount();
				//syscallLock->Release();
			}
		}
	}
}

void Signal_syscall(int conditionID, int lockID) {
	// if the lock ID is not between 0 and MAX_LOCKS-1, it's invalid
	if (lockID < 0 || lockID >= MAX_LOCKS) {
		//DEBUG('s', "[ERROR][Signal_syscall] Lock ID [%d] is invalid to wait.\n", lockID);
		printf("[ERROR][Signal_syscall] Lock ID [%d] is invalid to wait.\n", lockID);
		return;
	}
	// if the condition ID is not between 0 and MAX_CONDITIONS-1, it's invalid
	if (conditionID < 0 || conditionID >= MAX_CONDITIONS) {
		//DEBUG('s', "[ERROR][Signal_syscall] Condition ID [%d] is invalid to wait.\n", conditionID);
		printf("[ERROR][Signal_syscall] Condition ID [%d] is invalid to wait.\n", conditionID);
		return;
	}

	// now we do a thorough check against the condition ID and lock ID, lock ID first
	syscallLock->Acquire();
	//kernelLocksConditionsLock->Acquire();
	//kernelLocksLock->Acquire();

	if (!kernelLocksMap->Test(lockID)) {
		//DEBUG('s', "[ERROR][Signal_syscall] No Lock ID [%d] was allocated to wait.\n", lockID);
		printf("[ERROR][Signal_syscall] No Lock ID [%d] was allocated to wait.\n", lockID);
		syscallLock->Release();
		//kernelLocksConditionsLock->Release();
		//kernelLocksLock->Release();
		return;
	}
	else if (kernelLocks[lockID].addrSpace != currentThread->space) {
		//DEBUG('s', "[ERROR][Signal_syscall] Lock ID [%d] isn't associated with current thread/process to wait.\n", lockID);
		printf("[ERROR][Signal_syscall] Lock ID [%d] isn't associated with current thread/process to wait.\n", lockID);
		syscallLock->Release();
		//kernelLocksConditionsLock->Release();
		//kernelLocksLock->Release();
		return;
	}
	else {
		//kernelConditionsLock->Acquire();
		
		if (!kernelConditionsMap->Test(conditionID)) {
			//DEBUG('s', "[ERROR][Signal_syscall] No Condition ID [%d] was allocated to wait.\n", conditionID);
			printf("[ERROR][Signal_syscall] No Condition ID [%d] was allocated to wait.\n", conditionID);
			syscallLock->Release();
			//kernelLocksConditionsLock->Release();
			//kernelConditionsLock->Release();
			//kernelLocksLock->Release();
			return;
		}
		else {
			if (kernelConditions[conditionID].addrSpace != currentThread->space) {
				//DEBUG('s', "[ERROR][Signal_syscall] Condition ID [%d] isn't associated with current thread/process to wait.\n", conditionID);
				printf("[ERROR][Signal_syscall] Condition ID [%d] isn't associated with current thread/process to wait.\n", conditionID);
				syscallLock->Release();
				//kernelLocksConditionsLock->Release();
				//kernelConditionsLock->Release();
				//kernelLocksLock->Release();
				return;
			}
			// everything checks out, now we can signal the condition
			else {
				DEBUG('s', "[INFO][Signal_syscall] Condition ID [%d] is signaling with Lock ID [%d].\n", conditionID, lockID);
				kernelConditions[conditionID].condition->Signal(kernelLocks[lockID].lock);
				syscallLock->Release();
				//kernelLocksConditionsLock->Release();
				//kernelConditionsLock->Release();
				//kernelLocksLock->Release();
			}
		}
	}
}

void Broadcast_syscall(int conditionID, int lockID) {
	// if the lock ID is not between 0 and MAX_LOCKS-1, it's invalid
	if (lockID < 0 || lockID >= MAX_LOCKS) {
		//DEBUG('s', "[ERROR][Broadcast_syscall] Lock ID [%d] is invalid to broadcast.\n", lockID);
		printf("[ERROR][Broadcast_syscall] Lock ID [%d] is invalid to broadcast.\n", lockID);
		return;
	}
	// if the condition ID is not between 0 and MAX_CONDITIONS-1, it's invalid
	if (conditionID < 0 || conditionID >= MAX_CONDITIONS) {
		//DEBUG('s', "[ERROR][Broadcast_syscall] Condition ID [%d] is invalid to broadcast.\n", conditionID);
		printf("[ERROR][Broadcast_syscall] Condition ID [%d] is invalid to broadcast.\n", conditionID);
		return;
	}

	// now we do a thorough check against the condition ID and lock ID, lock ID first
	syscallLock->Acquire();
	//kernelLocksConditionsLock->Acquire();
	//kernelLocksLock->Acquire();

	if (!kernelLocksMap->Test(lockID)) {
		//DEBUG('s', "[ERROR][Broadcast_syscall] No Lock ID [%d] was allocated to broadcast.\n", lockID);
		printf("[ERROR][Broadcast_syscall] No Lock ID [%d] was allocated to broadcast.\n", lockID);
		syscallLock->Release();
		//kernelLocksConditionsLock->Release();
		//kernelLocksLock->Release();
		return;
	}
	else if (kernelLocks[lockID].addrSpace != currentThread->space) {
		//DEBUG('s', "[ERROR][Broadcast_syscall] Lock ID [%d] isn't associated with current thread/process to broadcast.\n", lockID);
		printf("[ERROR][Broadcast_syscall] Lock ID [%d] isn't associated with current thread/process to broadcast.\n", lockID);
		syscallLock->Release();
		//kernelLocksConditionsLock->Release();
		//kernelLocksLock->Release();
		return;
	}
	else {
		//kernelConditionsLock->Acquire();
		
		if (!kernelConditionsMap->Test(conditionID)) {
			//DEBUG('s', "[ERROR][Broadcast_syscall] No Condition ID [%d] was allocated to broadcast.\n", conditionID);
			printf("[ERROR][Broadcast_syscall] No Condition ID [%d] was allocated to broadcast.\n", conditionID);
			syscallLock->Release();
			//kernelLocksConditionsLock->Release();
			//kernelConditionsLock->Release();
			//kernelLocksLock->Release();
			return;
		}
		else {
			if (kernelConditions[conditionID].addrSpace != currentThread->space) {
				//DEBUG('s', "[ERROR][Broadcast_syscall] Condition ID [%d] isn't associated with current thread/process to broadcast.\n", conditionID);
				printf("[ERROR][Broadcast_syscall] Condition ID [%d] isn't associated with current thread/process to broadcast.\n", conditionID);
				syscallLock->Release();
				//kernelLocksConditionsLock->Release();
				//kernelConditionsLock->Release();
				//kernelLocksLock->Release();
				return;
			}
			// everything checks out, now we can broadcast the condition
			else {
				DEBUG('s', "[INFO][Broadcast_syscall] Condition ID [%d] is broadcasting with Lock ID [%d].\n", conditionID, lockID);
				kernelConditions[conditionID].condition->Broadcast(kernelLocks[lockID].lock);
				syscallLock->Release();
				//kernelLocksConditionsLock->Release();
				//kernelConditionsLock->Release();
				//kernelLocksLock->Release();
			}
		}
	}
}
#endif

//#endif // CHANGED
int IPTMissHandler(unsigned int vpn){
	int ppn = -1;
	
	//allocate a page of memory
	memoryMapLock->Acquire();
	ppn = memoryMap->Find();
	if(ppn == -1)		//memory is full, I should chose a page to evict
	{
		//printf("[ERROR] NO MORE MEMORY...\n");
		//memoryMapLock->Release();
		//return -1;
		
	}
	memoryMapLock->Release();


	//Read the page into memory from executable, if needed
	AddrSpace *currentSpace = currentThread->space;
	currentSpace->pageTableLock->Acquire();
	EnhancedTranslateEntry readEntry = currentSpace->pageTable[vpn];
	if(readEntry.location == EXECUTABLE)
	{
		currentSpace->processFile->ReadAt(&(machine->mainMemory[PageSize*ppn]),
					PageSize,readEntry.offset);
	}

	//update pageTable
	readEntry.physicalPage = ppn;
	readEntry.valid = TRUE;
	currentSpace->pageTableLock->Release();
	
	//update IPT
	iptLock->Acquire();
	ipt[ppn].physicalPage = ppn;
	ipt[ppn].virtualPage = vpn;
	ipt[ppn].valid= TRUE;
	ipt[ppn].iptSpace = currentThread->space;
	iptLock->Release();
	return ppn;
}

void PageFaultHandler(unsigned int vaddr){
		int vpn;
		//unsigned int tlbIndex;		
		vpn = vaddr/PageSize;		//get virtual page number
		int i;						
		int ppn = -1;				//physiical page number

		//IntStatus oldLevel = interrupt->SetLevel(IntOff);
		//currentTLB = (currentTLB+1)%TLBSize;
		//tlbIndex=(currentTLB++)%TLBSize;
		//machine->tlb[currentTLB].physicalPage=currentThread->space->getppn(vpn);
		
		for(i=0; i<NumPhysPages;i++)
		{
			//printf("current vpn: %d, ipt[i].virtualPage: %d\n",vpn, ipt[i].virtualPage);
			if(ipt[i].valid == TRUE && ipt[i].iptSpace== currentThread->space &&ipt[i].virtualPage == vpn)
			{
				 //this is for project3 step 2
				//machine->tlb[currentTLB].physicalPage = i;
				ppn = i;
				break;
			}
		}

		if(ppn == -1)
		{
			//ipt miss
			ppn = IPTMissHandler(vpn);
		}

		//TLB population from IPT Entry
		IntStatus oldLevel = interrupt->SetLevel(IntOff);
		currentTLB = (currentTLB+1)%TLBSize;
		machine->tlb[currentTLB].virtualPage= vpn;
		machine->tlb[currentTLB].physicalPage = ppn;
		machine->tlb[currentTLB].valid=TRUE;
		(void) interrupt->SetLevel(oldLevel);
		
		
}

void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall

    if ( which == SyscallException ) {
	switch (type) {
	    default:
		DEBUG('a', "Unknown syscall - shutting down.\n");
	    case SC_Halt:
		DEBUG('a', "Shutdown, initiated by user program.\n");
		interrupt->Halt();
		break;
	    case SC_Create:
		DEBUG('a', "Create syscall.\n");
		Create_Syscall(machine->ReadRegister(4),machine->ReadRegister(5));
		break;
	    case SC_Open:
		DEBUG('a', "Open syscall.\n");
		rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Write:
		DEBUG('a', "Write syscall.\n");
		Write_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
	    case SC_Read:
		DEBUG('a', "Read syscall.\n");
		rv = Read_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
	    case SC_Close:
		DEBUG('a', "Close syscall.\n");
		Close_Syscall(machine->ReadRegister(4));
		break;
//project 2
		case SC_CreateLock:
		DEBUG('a', "CreateLock.\n");
#ifdef NETWORK
		rv = CreateLock_syscall(machine->ReadRegister(4),
								 machine->ReadRegister(5));
#else
		rv = CreateLock_syscall();
#endif
		break;
		case SC_DestroyLock:
		DEBUG('a', "DestroyLock.\n");
		DestroyLock_syscall(machine->ReadRegister(4));
		break;
		case SC_Acquire:
		DEBUG('a', "Acquire.\n");
		Acquire_syscall(machine->ReadRegister(4));
		break;
		case SC_Release:
		DEBUG('a', "Release.\n");
		Release_syscall(machine->ReadRegister(4));
		break;
		case SC_CreateCV:
		DEBUG('a', "CreateCV.\n");
#ifdef NETWORK
		rv = CreateCV_syscall(machine->ReadRegister(4),
							   machine->ReadRegister(5));
#else
		rv = CreateCV_syscall();
#endif
		break;
		case SC_DestroyCV:
		DEBUG('a', "DestroyCV.\n");
		DestroyCV_syscall(machine->ReadRegister(4));
		break;
		case SC_Wait:
		DEBUG('a', "Wait.\n");
		Wait_syscall(machine->ReadRegister(4), 
					 machine->ReadRegister(5));
		break;
		case SC_Signal:
		DEBUG('a', "Signal.\n");
		Signal_syscall(machine->ReadRegister(4),
					   machine->ReadRegister(5));
		break;
		case SC_Broadcast:
		DEBUG('a', "Broadcast.\n");
		Broadcast_syscall(machine->ReadRegister(4),
						  machine->ReadRegister(5));
		break;
		case SC_Exec:
		DEBUG('a', "Exec.\n");
		rv = Exec_syscall(machine->ReadRegister(4), 
				  		  machine->ReadRegister(5));
		break;
		case SC_Fork:
		DEBUG('a', "Fork.\n");
		rv = Fork_syscall(machine->ReadRegister(4));
		break;
		case SC_Exit:
		DEBUG('a', "Exit.\n");
		Exit_syscall(machine->ReadRegister(4));
		break;
		case SC_Yield:
		DEBUG('a', "Yield.\n");
		Yield_syscall();
		break;
//project 3
/*		case SC_CreateLockN:
		DEBUG('a', "CreateLockN\n");	
		rv = CreateLockN_syscall(machine->ReadRegister(4),
								 machine->ReadRegister(5));
		break;
		case SC_DestroyLockN:
		DEBUG('a', "DestroyN\n");
		DestroyLockN_syscall(machine->ReadRegister(4));
		break;
		case SC_AcquireN:
		DEBUG('a',"AcquireN");
		AcquireN_syscall(machine->ReadRegister(4));
		break;
		case SC_ReleaseN:
		DEBUG('a',"ReleaseN");
		ReleaseN_syscall(machine->ReadRegister(4));
		break;
		case SC_CreateCVN:
		DEBUG('a', "CreateCVN\n");	
		rv = CreateCVN_syscall(machine->ReadRegister(4),
							   machine->ReadRegister(5));
		break;
		case SC_DestroyCVN:
		DEBUG('a', "DestroyCVN\n");
		DestroyCVN_syscall(machine->ReadRegister(4));
		break;
		case SC_WaitN:
		DEBUG('a', "WaitN.\n");
		waitN_syscall(machine->ReadRegister(4),
					  machine->ReadRegister(5));
		break;
		case SC_SignalN:
		DEBUG('a', "SignalN.\n");
		signalN_syscall(machine->ReadRegister(4),
						machine->ReadRegister(5));
		break;
		case SC_BroadcastN:
		DEBUG('a', "BroadcatN.\n");
		broadcastN_syscall(machine->ReadRegister(4),
						   machine->ReadRegister(5));
		break;
*/	
#ifdef NETWORK 
		case SC_CreateMVN:
		DEBUG('a', "CreateMVN\n");	
		rv = CreateMVN_syscall(machine->ReadRegister(4),
							   machine->ReadRegister(5),
							   machine->ReadRegister(6));
		break;
		case SC_DestroyMVN:
		DEBUG('a', "DestroyMVN\n");
		DestroyMVN_syscall(machine->ReadRegister(4));
		break;
		case SC_SETMV:
		DEBUG('a', "SETMV\n");	
		SetMV_syscall(machine->ReadRegister(4),
					  machine->ReadRegister(5),
					  machine->ReadRegister(6));
		break;
		case SC_GETMV:
		DEBUG('a', "GETMV\n");
		rv=GetMV_syscall(machine->ReadRegister(4),
						 machine->ReadRegister(5));
		break;	
		case SC_StopServer:
		DEBUG('a', "STOP\n");
		stopServer_syscall();
		break;	
#endif
	}

	// Put in the return value and increment the PC
	machine->WriteRegister(2,rv);
	machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
	machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
	machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
	return;
    }else if(which == PageFaultException){			//changed 
		PageFaultHandler(machine->ReadRegister(BadVAddrReg));	
    }else {
      cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
      interrupt->Halt();
    }
}
